package com.bzframework.pro.notify.email;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import com.bzframework.pro.notify.email.constants.MailConstants;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @Author bingzhong
 * @Date 2025/1/23 11:37
 * @Description
 */
@Slf4j
@RequiredArgsConstructor
public class DefaultEmailManager implements EmailManager {

    private final JavaMailSender javaMailSender;

    private final ReactiveRedisOperations<String, String> redisOperations;

    private final Configuration freeMarkerConfiguration;

    @Value("${spring.mail.from}")
    private String from;

    private static final String storageBasePath = "./data/email";

    @Override
    public Mono<Void> sendSimpleEmail(String to, String subject, String content) {

        return Mono.fromRunnable(() -> {
            SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
            simpleMailMessage.setFrom(from);
            simpleMailMessage.setTo(to);
            simpleMailMessage.setSubject(subject);
            simpleMailMessage.setText(content);
            javaMailSender.send(simpleMailMessage);
        });

    }

    @Override
    public Mono<Boolean> sendEmailCaptcha(String to) {

        if (StringUtils.isEmpty(to)) {
            return Mono.error(new RuntimeException("邮箱账号为空"));
        }
        if (!Validator.isEmail(to)) {
            return Mono.error(new RuntimeException("邮箱格式不合法"));
        }

        String mailLoginRepeatKey = MailConstants.getRedisKey(MailConstants.MAIL_LOGIN_CAPTCHA_REPEAT, to);
        redisOperations.hasKey(mailLoginRepeatKey)
                       .doOnNext(repeatStatus -> {
                           // 不存在则发送验证码,否则则忽略
                           if (!repeatStatus) {
                               sendCaptchaToSomebodyForLogin(to);
                           }
                       })
                       .subscribeOn(Schedulers.boundedElastic())
                       .subscribe();

        return Mono.just(true);
    }
    public void sendCaptchaToSomebodyForLogin(String to){

        String userMailCaptchaKey = MailConstants.getRedisKey(MailConstants.MAIL_LOGIN_CAPTCHA, to);
        redisOperations.opsForValue()
                       .get(userMailCaptchaKey)
                       .switchIfEmpty(Mono.defer(() -> {
                           String userMailCaptcha = RandomUtil.randomString(4).toUpperCase();
                           return redisOperations.opsForValue()
                                                 .set(userMailCaptchaKey, userMailCaptcha, Duration.ofMinutes(10))
                                                 .thenReturn(userMailCaptcha);
                       }))
                       .flatMap(userMailCaptcha -> {
                           // 每次发送邮箱验证码时设置1分钟内防止重复发送
                           String userMailCaptchaRepeatKey = MailConstants.getRedisKey(MailConstants.MAIL_LOGIN_CAPTCHA_REPEAT, to);
                           return redisOperations.opsForValue()
                                                 .set(userMailCaptchaRepeatKey,"",Duration.ofMinutes(1))
                                                 .thenReturn(userMailCaptcha);
                       })
                       .flatMap(userMailCaptcha -> doSendEmail(to, userMailCaptcha))
                       .subscribeOn(Schedulers.boundedElastic())
                       .subscribe();
    }

    /**
     * 发送邮件
     * @param to 发给谁
     * @param userMailCaptcha 验证码
     * @return
     */
    private Mono<Boolean> doSendEmail(String to, String userMailCaptcha) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
        try {
            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(to);
            mimeMessageHelper.setSubject("平台登录验证码");
            String emailTemplate = createEmailTemplate(to, userMailCaptcha);
            mimeMessageHelper.setText(emailTemplate,true);
        } catch (MessagingException e) {
            return Mono.error(new RuntimeException("发送邮件失败:" + to));
        }
        javaMailSender.send(mimeMessage);
        return Mono.just(true);
    }


    /**
     * 创建邮件模板
     * @param to 发给谁
     * @param captcha 验证码
     * @return
     */
    private String createEmailTemplate(String to, String captcha){
        String emailTemplatePath = "";
        try {
            Template template = freeMarkerConfiguration.getTemplate("EmailTemplate.html.ftl");
            String storagePath = RandomUtil.randomString(10) + ".html";
            Path path = Paths.get(storageBasePath, storagePath);
            path.toFile().getParentFile().mkdirs();
            FileWriter writer = new FileWriter(path.toFile());
            Map<String,Object> freeMarkerValueMap = new HashMap<>(2);
            List<String> verifyCodes = Arrays.stream(captcha.split("")).collect(Collectors.toList());
            freeMarkerValueMap.put("verifyCodes",verifyCodes);
            freeMarkerValueMap.put("userName",to);
            // 生成验证码邮件模板文件
            template.process(freeMarkerValueMap,writer);
            writer.close();
            emailTemplatePath = path.toFile().getAbsolutePath();
            return FileUtil.readString(emailTemplatePath, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("构建邮件模板失败:{},{}",to, e.getMessage());
            throw new RuntimeException("构建邮件模板失败:" + to);
        }finally {
            // 删除文件
            if (FileUtil.exist(emailTemplatePath)) {
                FileUtil.del(emailTemplatePath);
            }
        }
    }
}
